/*
 * NoseConeHandler.java
 */
package info.openrocket.core.file.rocksim.importt;

import java.util.HashMap;

import info.openrocket.core.logging.WarningSet;
import info.openrocket.core.file.DocumentLoadingContext;
import info.openrocket.core.file.rocksim.RockSimCommonConstants;
import info.openrocket.core.file.rocksim.RockSimFinishCode;
import info.openrocket.core.file.rocksim.RockSimNoseConeCode;
import info.openrocket.core.file.simplesax.ElementHandler;
import info.openrocket.core.file.simplesax.PlainTextHandler;
import info.openrocket.core.material.Material;
import info.openrocket.core.rocketcomponent.NoseCone;
import info.openrocket.core.rocketcomponent.RocketComponent;
import info.openrocket.core.rocketcomponent.Transition;

import org.xml.sax.SAXException;

/**
 * The SAX nose cone handler for Rocksim NoseCones.
 */
class NoseConeHandler extends BaseHandler<NoseCone> {

	/**
	 * The OpenRocket NoseCone.
	 */
	private final NoseCone noseCone = new NoseCone();

	/**
	 * The wall thickness. Used for hollow nose cones.
	 */
	private double thickness = 0.0d;

	/**
	 * Constructor.
	 *
	 * @param c        the parent component to the nosecone
	 * @param warnings the warning set
	 * 
	 * @throws IllegalArgumentException thrown if <code>c</code> is null
	 */
	public NoseConeHandler(DocumentLoadingContext context, RocketComponent c, WarningSet warnings)
			throws IllegalArgumentException {
		super(context);
		if (c == null) {
			throw new IllegalArgumentException("The parent component of a nose cone may not be null.");
		}
		if (isCompatible(c, NoseCone.class, warnings)) {
			c.addChild(noseCone);
			noseCone.setAftRadiusAutomatic(false);
		}
	}

	@Override
	public ElementHandler openElement(String element, HashMap<String, String> attributes, WarningSet warnings) {
		// Nose cones in Rocksim may have attached parts - namely Mass Objects - as
		// children.
		if (RockSimCommonConstants.ATTACHED_PARTS.equals(element)) {
			return new AttachedPartsHandler(context, noseCone);
		}
		return PlainTextHandler.INSTANCE;
	}

	@Override
	public void closeElement(String element, HashMap<String, String> attributes,
			String content, WarningSet warnings) throws SAXException {
		super.closeElement(element, attributes, content, warnings);

		try {
			if (RockSimCommonConstants.SHAPE_CODE.equals(element)) {
				noseCone.setShapeType(RockSimNoseConeCode.fromCode(Integer.parseInt(content)).asOpenRocket());
			}
			if (RockSimCommonConstants.LEN.equals(element)) {
				noseCone.setLength(
						Math.max(0, Double.parseDouble(content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH));
			}
			if (RockSimCommonConstants.BASE_DIA.equals(element)) {
				noseCone.setAftRadius(
						Math.max(0, Double.parseDouble(content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS));
			}
			if (RockSimCommonConstants.WALL_THICKNESS.equals(element)) {
				thickness = Math.max(0,
						Double.parseDouble(content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH);
			}
			if (RockSimCommonConstants.SHOULDER_OD.equals(element)) {
				noseCone.setAftShoulderRadius(Math.max(0, Double.parseDouble(
						content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_RADIUS));
			}
			if (RockSimCommonConstants.SHOULDER_LEN.equals(element)) {
				noseCone.setAftShoulderLength(Math.max(0, Double.parseDouble(
						content) / RockSimCommonConstants.ROCKSIM_TO_OPENROCKET_LENGTH));
			}
			if (RockSimCommonConstants.SHAPE_PARAMETER.equals(element)) {
				// The Rocksim ShapeParameter only applies to certain shapes, although it is
				// included
				// in the design file for all nose cones. Applying it when it should not be
				// causes oddities so
				// a check is made for the allowable shapes.
				if (Transition.Shape.POWER.equals(noseCone.getShapeType()) ||
						Transition.Shape.HAACK.equals(noseCone.getShapeType()) ||
						Transition.Shape.PARABOLIC.equals(noseCone.getShapeType())) {
					noseCone.setShapeParameter(Double.parseDouble(content));
				}
			}
			if (RockSimCommonConstants.CONSTRUCTION_TYPE.equals(element)) {
				int typeCode = Integer.parseInt(content);
				if (typeCode == 0) {
					// SOLID
					noseCone.setFilled(true);
				} else if (typeCode == 1) {
					// HOLLOW
					noseCone.setFilled(false);
				}
			}
			if (RockSimCommonConstants.FINISH_CODE.equals(element)) {
				noseCone.setFinish(RockSimFinishCode.fromCode(Integer.parseInt(content)).asOpenRocket());
			}
			if (RockSimCommonConstants.MATERIAL.equals(element)) {
				setMaterialName(content);
			}
		} catch (NumberFormatException nfe) {
			warnings.add("Could not convert " + element + " value of " + content + ".  It is expected to be a number.");
		}
	}

	@Override
	public void endHandler(String element, HashMap<String, String> attributes, String content, WarningSet warnings)
			throws SAXException {
		super.endHandler(element, attributes, content, warnings);

		if (noseCone.isFilled()) {
			noseCone.setAftShoulderThickness(noseCone.getAftShoulderRadius());
		} else {
			noseCone.setThickness(thickness);
			noseCone.setAftShoulderThickness(thickness);
		}
	}

	/**
	 * Get the nose cone component this handler is working upon.
	 *
	 * @return a nose cone component
	 */
	@Override
	public NoseCone getComponent() {
		return noseCone;
	}

	/**
	 * Get the required type of material for this component.
	 *
	 * @return BULK
	 */
	@Override
	public Material.Type getMaterialType() {
		return Material.Type.BULK;
	}

}
